import re
import os
import sys
import random
import logging

from PyQt5.QtWidgets import (QAction, QAbstractItemView, QDialog, QFrame, QHBoxLayout, QLabel, QMenu, QPushButton,
                             QSlider,
                             QTableWidget, QTableWidgetItem, QVBoxLayout, QApplication)
from PyQt5.QtGui import QBrush, QColor, QCursor, QPixmap, QFont, QPainter, QPen, QLinearGradient
from PyQt5.QtCore import QUrl, Qt, QObject, QPropertyAnimation, QPoint, QRect, QEasingCurve, QAbstractAnimation, QTime, \
    QTimer, QRegExp, QRectF
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QMediaMetaData, QMediaPlaylist

import addition
from base import (blur, checkFolder, cacheFolder, checkOneFolder, centerHTML, HBoxLayout, HStretchBox,
                  pickle, PicLabel, pyqtSignal, QTextEdit, ScrollArea, VBoxLayout)
from asyncBase import aAsync, toTask
from netEaseApi import netease
from xiamiApi import xiami
from qqApi import qqApi

from desktopLyricButtons import *


def _fromUtf8(s):
    return s


QString = str
logger = logging.getLogger(__name__)


class PlayWidgets(QFrame):

    def __init__(self, parent=None):
        super(PlayWidgets, self).__init__()
        self.setObjectName('playwidget')
        self.parent = parent
        self.module = 0

        with open('QSS/playWidgets.qss', 'r', encoding='utf-8') as f:
            self.setStyleSheet(f.read())

        self.playList = PlayList(self)
        self.currentMusic = CurrentMusic(self)
        self.player = Player(self)
        self.desktopLyric = DesktopLyric(self)
        self.setButtons()
        self.setLabels()
        self.setSliders()
        self.setLayouts()
        self.loadCookies()

    def setButtons(self):
        self.previousButton = QPushButton(self)
        self.previousButton.setObjectName("previousButton")
        self.previousButton.clicked.connect(self.previousSing)
        self.playButton = QPushButton(self)
        self.playButton.setObjectName("playButton")
        self.playButton.clicked.connect(lambda: self.playEvent(self.player))
        self.pauseButton = QPushButton(self)
        self.pauseButton.setObjectName("pauseButton")
        self.pauseButton.clicked.connect(lambda: self.pauseEvent(self.player))
        self.pauseButton.hide()
        self.nextButton = QPushButton(self)
        self.nextButton.setObjectName("nextButton")
        self.nextButton.clicked.connect(self.nextSing)
        self.volume = QPushButton(self)
        self.volume.setObjectName("volume")
        self.volume.clicked.connect(self.volumeEvent)
        self.noVolume = QPushButton(self)
        self.noVolume.setObjectName("no_volume")
        self.noVolume.hide()
        self.noVolume.clicked.connect(self.noVolumeEvent)
        self.single = QPushButton(self)
        self.single.setObjectName("single")
        self.single.hide()
        self.single.setToolTip("单曲循环")
        self.single.clicked.connect(self.singleEvent)
        self.repeat = QPushButton(self)
        self.repeat.setObjectName("repeat")
        self.repeat.setToolTip("列表循环")
        self.repeat.clicked.connect(self.repeatEvent)
        self.shuffle = QPushButton(self)
        self.shuffle.setObjectName("shuffle")
        self.shuffle.hide()
        self.shuffle.setToolTip("随机播放")
        self.shuffle.clicked.connect(self.shuffleEvent)
        self.lyricButton = QPushButton(self)
        self.lyricButton.setText("词")
        self.lyricButton.setToolTip("打开歌词")
        self.lyricButton.setObjectName("lyricButton")
        self.lyricButton.clicked.connect(self.lyricEvent)
        self.playlist = QPushButton(self)
        self.playlist.setObjectName("playlist")
        self.playlist.clicked.connect(self.playlistEvent)

    def setLabels(self):
        self.currentTime = QLabel(self)
        self.countTime = QLabel(self)
        self.currentTime.setText('01:09')
        self.countTime.setText('03:04')

    def setSliders(self):
        self.slider = QSlider(self)
        self.slider.setMinimumHeight(5)
        self.slider.setMinimumWidth(440)
        self.slider.setRange(0, 1000)
        self.slider.setObjectName("slider")
        self.slider.setOrientation(Qt.Horizontal)
        self.slider.sliderReleased.connect(self.sliderEvent)
        self.slider.sliderPressed.connect(self.sliderPressEvent)
        self.volumeSlider = QSlider(self)
        self.volumeSlider.setValue(100)
        self.volumeSlider.setMinimumHeight(5)
        self.volumeSlider.setObjectName("volumeSlider")
        self.volumeSlider.setOrientation(Qt.Horizontal)
        self.volumeSlider.valueChanged.connect(self.volumeChangedEvent)

    def setLayouts(self):
        self.mainLayout = QHBoxLayout()
        self.mainLayout.addWidget(self.previousButton)
        self.mainLayout.addWidget(self.playButton)
        self.mainLayout.addWidget(self.pauseButton)
        self.mainLayout.addWidget(self.nextButton)
        self.mainLayout.addWidget(self.currentTime)
        self.mainLayout.addWidget(self.slider)
        self.mainLayout.addWidget(self.countTime)
        self.mainLayout.addWidget(self.volume)
        self.mainLayout.addWidget(self.noVolume)
        self.mainLayout.addWidget(self.volumeSlider)
        self.mainLayout.addSpacing(10)
        self.mainLayout.addWidget(self.single)
        self.mainLayout.addWidget(self.repeat)
        self.mainLayout.addWidget(self.shuffle)
        self.mainLayout.addSpacing(10)
        self.mainLayout.addWidget(self.lyricButton)
        self.mainLayout.addSpacing(10)
        self.mainLayout.addWidget(self.playlist)
        self.mainLayout.addStretch(1)
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self.mainLayout)

    def previousSing(self):
        self.player.playList.previous()

    def nextSing(self):
        self.player.playList.next()

    def setPlayerAndPlayList(self, data: dict, index=0):
        logger.info("尝试添加歌曲到播放列表，歌曲信息: {0} \n".format(data))
        authorAndName = data['author'] + data['name']
        for i, mediaInfo in enumerate(self.playList.musicList):
            checkAuthorAndName = mediaInfo['author'] + mediaInfo['name']

            if authorAndName == checkAuthorAndName:
                if self.player.playList.currentIndex == i and i != 0:
                    return
                self.player.setIndex(i)
                return

        sureSetUp = self.player.setMusic(data['url'], data)
        if not sureSetUp:
            self.currentMusic.setShortInfo('音乐不可播放', data['author'], data['music_img'])
            return
        self.playList.addMusic(data)
        self.player.playList.tabMusicEvent()
        self.playList.addPlayList(data['name'], data['author'], data['time'])

    def setPlayerAndPlaylists(self, datas: list):
        datas = datas[::-1]
        for i in datas:
            self.playList.addPlayList(i['name'], i['author'], i['time'])
        self.playList.addMusics(datas)
        self.player.setAllMusics(datas)
        sureSetUp = self.player.setMusic(i['url'], i)
        if not sureSetUp:
            self.currentMusic.setShortInfo('音乐不可播放', data['author'], data['music_img'])
            self.nextSing()
            return
        self.player.playList.tabMusicEvent()

    def saveCookies(self):
        self.playList.saveCookies()
        self.player.saveCookies()

    def loadCookies(self):
        self.playList.loadCookies()
        self.player.loadCookies()

    def playEvent(self, media):
        self.playButton.hide()
        self.pauseButton.show()
        media.play()

    def pauseEvent(self, media):
        self.pauseButton.hide()
        self.playButton.show()
        media.pause()

    def stopEvent(self, media):
        self.pauseButton.hide()
        self.playButton.show()
        media.stop()

    def volumeEvent(self):
        self.volume.hide()
        self.noVolume.show()
        self.volumeSlider.setValue(0)

    def noVolumeEvent(self):
        self.noVolume.hide()
        self.volume.show()
        self.volumeSlider.setValue(100)

    def volumeChangedEvent(self):
        self.player.setVolume(self.volumeSlider.value())

    def singleEvent(self):
        self.single.hide()
        self.shuffle.show()
        self.player.playList.setPlaybackMode(4)
        self.module = 2

    def repeatEvent(self):
        self.repeat.hide()
        self.single.show()
        self.player.playList.setPlaybackMode(1)
        self.module = 1

    def shuffleEvent(self):
        self.shuffle.hide()
        self.repeat.show()
        self.player.playList.setPlaybackMode(3)
        self.module = 0

    def lyricEvent(self):
        lyricStyle = """
                QPushButton#lyricButton {{
                    width: 10px;
                    height: 10px;
                    margin: 0px;
                    padding: 3px;
                    background: {};
                    color: {};
                    border: 1px solid #919192;
                }}
            QPushButton#lyricButton:hover {{
                background: none;
                color: #DCDCDC;
                border-color: #DCDCDC;
            }}
                """
        if self.desktopLyric.isVisible():
            self.lyricButton.setToolTip("打开歌词")
            self.lyricButton.setStyleSheet(lyricStyle.format("none", "#79797B"))
            return self.desktopLyric.hide()

        self.lyricButton.setToolTip("关闭歌词")
        self.lyricButton.setStyleSheet(lyricStyle.format("#828282", "#FFFFFF"))
        self.desktopLyric.show()
        self.currentMusic.setDetailInfo()

    def playlistEvent(self):
        if self.playList.isVisible():
            self.playList.hide()
        else:
            self.playList.move(self.parent.width() - 574, self.parent.height() - 477 - self.height())
            self.playList.show()
            self.playList.raise_()

    def sliderEvent(self):
        value = self.slider.value()
        currentSliderTime = self.player.allTime() * value
        self.player.setPosition(currentSliderTime)
        self.player.setConnects()
        self.slider.sliderMoved.disconnect()

    def sliderPressEvent(self):
        self.player.setDisconnects()
        self.slider.sliderMoved.connect(self.sliderMovedEvent)

    def sliderMovedEvent(self):
        value = self.slider.value()
        currentSliderTime = self.player.allTime() * value / 1000
        currentSliderTime = addition.itv2time(currentSliderTime)
        self.currentTime.setText(currentSliderTime)
        self.player.timeChanged.emit(currentSliderTime)


class PlayList(QFrame):
    musicListCookiesFolder = 'cookies/playlist/musicList.cks'
    allCookiesFolder = [musicListCookiesFolder]

    def __init__(self, parent=None):
        super(PlayList, self).__init__()
        self.parent = parent
        self.setParent(self.parent.parent)
        self.setObjectName("PlayList")
        self.musicList = []
        self.currentRow = -1
        self.allRow = 0
        self.itemColor = QBrush(QColor.fromRgb(95, 95, 99))
        with open('QSS/playList.qss', 'r') as f:
            self.setStyleSheet(f.read())
        self.resize(574, 477)
        self.hide()
        self.setButtons()
        self.setLabels()
        self.setTables()
        self.setLayouts()

    def setButtons(self):
        self.closeButton = QPushButton("×", self)
        self.closeButton.setObjectName("closeButton")
        self.closeButton.clicked.connect(self.hide)

    def setLabels(self):
        pass

    def setTables(self):
        self.playList = _TableWidget(self)
        self.playList.setMinimumWidth(self.width())
        self.playList.setColumnCount(3)
        self.playList.setColumnWidth(0, self.width() / 3 * 2)
        self.playList.horizontalHeader().setVisible(False)
        self.playList.horizontalHeader().setStretchLastSection(True)
        self.playList.verticalHeader().setVisible(False)
        self.playList.setShowGrid(False)
        self.playList.setAlternatingRowColors(True)
        self.playList.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.playList.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.playList.itemDoubleClicked.connect(self.play)

    def setLayouts(self):
        self.mainLayout = QVBoxLayout()
        self.headerLayout = QHBoxLayout()
        self.headerLayout.addStretch(1)
        self.headerLayout.addWidget(self.closeButton)
        self.mainLayout.addLayout(self.headerLayout)
        self.mainLayout.addWidget(self.playList)
        self.setLayout(self.mainLayout)

    def play(self):
        if self.currentRow == self.playList.currentRow():
            return
        else:
            currentRow = self.playList.currentRow()
            logger.info("尝试播放已在播放列表的歌曲，歌曲位置索引{0}.".format(currentRow))
            currentMusic = self.musicList[currentRow]
            self.parent.player.setIndex(currentRow)
            self.parent.currentMusic.setShortInfo(currentMusic['name'], currentMusic['author'])
            self.currentRow = currentRow

    def clear(self):
        self.playList.clears()

    def addMusic(self, data):
        self.musicList.append(data)

    def addMusics(self, mlist):
        self.musicList.extend(mlist)

    def addPlayList(self, name, author, time):
        self.playList.setRowCount(self.allRow + 1)
        musicName = QTableWidgetItem(name)
        self.playList.setItem(self.allRow, 0, musicName)
        musicAuthor = QTableWidgetItem(author)
        musicAuthor.setForeground(self.itemColor)
        self.playList.setItem(self.allRow, 1, musicAuthor)
        musicTime = QTableWidgetItem(time)
        musicTime.setForeground(self.itemColor)
        self.playList.setItem(self.allRow, 2, musicTime)
        self.allRow += 1

    @checkFolder(allCookiesFolder)
    def saveCookies(self):
        with open(self.musicListCookiesFolder, 'wb') as f:
            pickle.dump(self.musicList, f)

    @checkFolder(allCookiesFolder)
    def loadCookies(self):
        with open(self.musicListCookiesFolder, 'rb') as f:
            self.musicList = pickle.load(f)

        for i in self.musicList:
            self.addPlayList(i['name'], i['author'], i['time'])


class CurrentMusic(QFrame):

    def __init__(self, parent=None):
        super(CurrentMusic, self).__init__()
        self.parent = parent
        self.grandparent = self.parent.parent
        self.setParent(self.grandparent)
        self.setObjectName("currentMusic")

        with open('QSS/currentMusic.qss', 'r') as f:
            self.setStyleSheet(f.read())

        self.currentMusicId = 0
        self.order = -2
        self.count = 0
        self.lyricCache = ''
        self.raise_()
        self.activateWindow()
        self.shortInfo = CurrentMusicShort(self)
        self.detailInfo = CurrentMusicDetail(self)
        self.showDetail = 'animation'
        self.showShort = 'animation'
        self.mousePos = None
        self.mainLayout = QHBoxLayout(self)
        self.mainLayout.addWidget(self.shortInfo)
        self.mainLayout.addWidget(self.detailInfo)
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.mainLayout.setSpacing(0)
        self.setFeatures()

    @checkOneFolder(cacheFolder)
    def setShortInfo(self, name=None, author=None, pic=None):

        if pic:
            cacheList = os.listdir(cacheFolder)
            names = str(pic[pic.rfind('/') + 1:])
            if names in cacheList:
                pic = cacheFolder + '/' + names

            self.shortInfo.musicPic.setSrc(pic)
        self.shortInfo.musicName.setText(name)
        self.shortInfo.musicAuthor.setText(author)

    def setDetailInfo(self):
        title = self.shortInfo.musicName.text()
        picSrc = self.shortInfo.musicPic.getSrc()
        blurSrc = blur(picSrc)
        self.detailInfo.titleLabel.setText(title)
        if blurSrc:
            self.detailInfo.lyricFrames.setStyleSheet('''
            QScrollArea#lyricScroll {{
                background-image: url({0});
            }}
                '''.format(blurSrc))
        self.showLyric()

    def getDetailInfo(self):
        self.shortInfo.hide()
        self.detailInfo.show()
        self.showDetail = QPropertyAnimation(self, b"geometry")
        x = self.pos().x()
        y = self.pos().y()
        self.showDetail.setStartValue(QRect(x, y, self.width(), self.height()))
        self.showDetail.setEndValue(QRect(0, self.grandparent.header.height() + 3, self.grandparent.width(),
                                          self.grandparent.mainContent.height()))
        self.showDetail.setDuration(300)
        self.showDetail.setEasingCurve(QEasingCurve.InBack)
        self.showDetail.start(QAbstractAnimation.DeleteWhenStopped)
        self.raise_()
        self.setDetailInfo()

    def showLyric(self):
        lyric = self.getLyric()
        lyric.add_done_callback(self.lyricCallback)

    def lyricCallback(self, future):

        self.detailInfo.removeAllLyricLabels()
        self.count = 0
        self.order = -2
        result = future.result().split('\n')
        self.parent.player.lrc_lst = getLyric(result)
        signal = self.parent.player.timeChanged

        for x, i in enumerate(result):
            data = re.findall(r'\[+(.*?)\]+(.*)', i)
            if not data:
                if not i:
                    continue
                else:
                    self.detailInfo.addLyricLabel(_LyricLabel('00:00', i, x, signal, self))
                    continue
            if not data[0][1]:
                self.detailInfo.addLyricLabel(_LyricLabel(data[0][0], '\n', x, signal, self))
                continue
            self.detailInfo.addLyricLabel(_LyricLabel(data[0][0], data[0][1], x, signal, self))
        self.count = x

    def slide(self):
        maxValue = round(self.detailInfo.lyricFrames.maximumValue() / self.count) * self.order
        self.detailInfo.lyricFrames.verticalScrollBar().setValue(maxValue)

    def unLightLyric(self):
        if self.order < 0:
            return
        self.detailInfo.allLyrics[self.order].unLightMe()

    @toTask
    def getLyric(self):
        musicInfo = self.parent.player.getCurrentMusicInfo()
        logger.info("尝试获取歌曲歌词地址。 当前歌曲信息: {0}".format(musicInfo))
        if not musicInfo:
            return "请慢慢欣赏"

        if 'http' not in musicInfo.get('url'):
            future = aAsync(xiami.search, musicInfo.get('name') + ' ' + musicInfo.get('author'))
            data = yield from future
            lyricUrl = data['songs'][0].get('lyric')
            future = aAsync(xiami.lyric, lyricUrl)
            data = yield from future
            self.lyricCache = data
            return data

        musicId = musicInfo.get('music_id')
        if self.currentMusicId == musicId:
            return self.lyricCache
        lyricUrl = musicInfo.get('lyric')

        if not lyricUrl:
            future = aAsync(netease.lyric, musicId)
            data = yield from future
        else:
            if 'xiami' in lyricUrl:
                future = aAsync(xiami.lyric, lyricUrl)
                data = yield from future
            elif lyricUrl == 'qq':

                return "请慢慢欣赏"

        if not data:
            self.currentMusicId = musicId
            return "请慢慢欣赏"
        self.currentMusicId = musicId
        self.lyricCache = data
        return data

    def getShortInfo(self):
        self.detailInfo.hide()
        self.showShort = QPropertyAnimation(self, b"geometry")
        x = self.pos().x()
        y = self.pos().y()
        self.showShort.setStartValue(
            QRect(0, self.grandparent.header.height(), self.grandparent.width(), self.grandparent.mainContent.height()))
        self.showShort.setEndValue(
            QRect(0, self.grandparent.height() - 64 - self.parent.height(), self.grandparent.navigation.width(), 64))
        self.showShort.setDuration(300)
        self.showShort.setEasingCurve(QEasingCurve.InBack)
        self.showShort.start(QAbstractAnimation.DeleteWhenStopped)
        self.shortInfo.show()
        self.raise_()

    def setFeatures(self):
        self.detailInfo.recoveryButton.clicked.connect(self.getShortInfo)

    def mousePressEvent(self, event):
        self.mousePos = QCursor.pos()

    def mouseReleaseEvent(self, evnet):
        if QCursor.pos() != self.mousePos:
            return
        else:
            self.getDetailInfo()


class CurrentMusicShort(QFrame):

    def __init__(self, parent=None):
        super(CurrentMusicShort, self).__init__()
        self.parent = parent
        self.setObjectName('short')
        self.mousePos = None
        self.setLabels()
        self.setLayouts()
        self.init()

    def setLabels(self):
        self.musicName = QLabel(self)
        self.musicName.adjustSize()
        self.musicAuthor = QLabel(self)
        self.musicPic = PicLabel('resource/no_music.png', 64, 64)
        self.musicPic.setObjectName("musicPic")
        self.musicMask = PicLabel('resource/expand.png', 64, 64)
        self.musicMask.hide()
        self.musicMask.setStyleSheet('QLabel {background-color: rgba(0, 0, 0, 50%;)}')
        self.musicLayout = VBoxLayout(self.musicPic)
        self.musicLayout.addWidget(self.musicMask)

    def setLayouts(self):
        self.mainLayout = QHBoxLayout()
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.musicInfo = QVBoxLayout()
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.musicInfo.addWidget(self.musicName)
        self.musicInfo.addWidget(self.musicAuthor)
        self.mainLayout.addWidget(self.musicPic)
        self.mainLayout.addLayout(self.musicInfo)
        self.mainLayout.setStretch(0, 1)
        self.mainLayout.setStretch(1, 2)
        self.setLayout(self.mainLayout)

    def init(self):
        self.musicName.setText("Enjoy it")
        self.musicAuthor.setText("Enjoy it")

    def enterEvent(self, event):
        if not self.musicMask.isVisible():
            self.musicMask.show()

    def leaveEvent(self, event):
        if self.musicMask.isVisible():
            self.musicMask.hide()


class CurrentMusicDetail(ScrollArea):

    def __init__(self, parent):
        super(CurrentMusicDetail, self).__init__()
        self.setObjectName('detail')
        self.hide()
        self.mainLayout = VBoxLayout(self.frame)
        self.topLayout = HBoxLayout()
        self.topMainLayout = VBoxLayout()
        self.topHeaderLayout = HBoxLayout()
        self.lyricFrames = ScrollArea()
        self.lyricFrames.setObjectName('lyricScroll')
        self.lyricFrames.frame.setObjectName('lyricFrame')
        self.lyricFramesLayout = VBoxLayout(self.lyricFrames.frame)
        self.allLyrics = []
        self.titleLabel = QLabel("~~~")
        self.titleLabel.setObjectName('titleLabel')
        self.recoveryButton = QPushButton()
        self.recoveryButton.setObjectName('recoveryButton')
        self.recoveryButton.setMinimumSize(24, 24)
        self.recoveryButton.setMaximumSize(36, 36)
        self.setLayouts()

    def setLayouts(self):
        self.mainLayout.addLayout(self.topLayout)
        self.topLayout.addLayout(self.topMainLayout)
        self.topMainLayout.addSpacing(25)
        self.topMainLayout.addLayout(self.topHeaderLayout)
        self.topHeaderLayout.addStretch(1)
        self.topHeaderLayout.addSpacing(100)
        self.topHeaderLayout.addWidget(self.titleLabel)
        self.topHeaderLayout.addStretch(1)
        self.topHeaderLayout.addSpacing(20)
        self.topHeaderLayout.addWidget(self.recoveryButton)
        self.topHeaderLayout.addSpacing(50)
        self.topMainLayout.addSpacing(30)
        self.topMainLayout.addWidget(self.lyricFrames)

    def addLyricLabel(self, label):
        HStretchBox(self.lyricFramesLayout, label)
        self.allLyrics.append(label)

    def removeAllLyricLabels(self):
        for i in self.allLyrics:
            i.deleteLater()
        self.allLyrics = []

        for i in range(self.lyricFramesLayout.count()):
            self.lyricFramesLayout.takeAt(i)


class Player(QMediaPlayer):
    timeChanged = pyqtSignal(str)

    def __init__(self, parent=None):
        super(Player, self).__init__()
        self.setObjectName('player')
        self.parent = parent
        self.playWidgets = self.parent
        self.transTime = addition.itv2time
        self.musicTime = 0
        self.playList = _MediaPlaylist(self)
        self.playList.setPlaybackMode(self.playList.Loop)
        self.lrc_lst = []
        self.setConnects()

    def setConnects(self):
        self.durationChanged.connect(self.countTimeEvent)
        self.positionChanged.connect(self.positionChangedEvent)
        self.stateChanged.connect(self.stateChangedEvent)
        self.error.connect(self.dealError)
        self.playList.setInitConnection()

    def setDisconnects(self):
        self.durationChanged.disconnect()
        self.positionChanged.disconnect()
        self.stateChanged.disconnect()
        self.error.disconnect()
        self.mediaStatusChanged.disconnect()

    def setMusic(self, url, data):
        if url:
            if 'http' in url or 'file' in url:
                self.playList.addMedias(QMediaContent(QUrl(url)), data)
            else:
                self.playList.addMedias(QMediaContent(QUrl.fromLocalFile(url)), data)
            return True
        return False

    def setAllMusics(self, datas):
        self.playList.addAllMedias(datas)

    def setIndex(self, index):
        self.playList.setCurrentIndex(index)

    def allTime(self):
        return self.duration() / 1000

    def getCurrentMusicInfo(self):

        return self.playList.mediaList.get(self.currentMedia().canonicalUrl().toString())

    def playMusic(self, url=None, data=None):
        if url:
            self.setMusic(url, data)
            self.playWidgets.playEvent(self)
        else:
            self.playWidgets.playEvent(self)

    def dealError(self, error):
        if error:
            try:
                musicInfo = self.playList.mediaList.pop(self.currentMedia().canonicalUrl().toString())
                self.loadRealMusicUrl(musicInfo)
            except:
                logger.error("尝试重新获取音乐地址出错。", exc_info=True)
                print('尝试重新获取音乐地址出错，请清空或删除无效的音乐信息后重试。')

    @toTask
    def loadRealMusicUrl(self, musicInfo):
        musicId = musicInfo.get('music_id')

        logger.info("歌曲地址失效，尝试重新获取，id号: {0}".format(musicId))

        if not musicId:
            self.playWidgets.nextSing()
            return

        future = aAsync(netease.singsUrl, [musicId])

        musicUrl = musicInfo.get('url')
        if musicUrl.find('C40000') != -1 or musicUrl.find('qqmusic') != -1:
            future = aAsync(qqApi.getSongUrl, musicUrl)

        data = yield from future

        if not data:
            self.playWidgets.nextSing()
            return

        logger.info("id {0}'s response: {1}".format(musicId, data))

        if not data:
            print("获取音乐地址失败，请检查网络后重试。")
            self.playList.mediaList[url] = musicInfo
            return

        url = data[0]['url']
        musicInfo['url'] = url
        self.playList.mediaList[url] = musicInfo
        self.playList.musics[self.playList.currentIndex] = url
        self.playList.play()

    def saveCookies(self):
        self.playList.saveCookies()

    def loadCookies(self):
        self.playList.loadCookies()

    def countTimeEvent(self, duration):
        self.musicTime = duration / 1000
        self.playWidgets.countTime.setText(self.transTime(self.musicTime))
        self.playList.duration = duration

    def positionChangedEvent(self, position):
        currentTime = self.position() / 1000
        transedTime = self.transTime(currentTime)
        self.playWidgets.currentTime.setText(transedTime)
        self.timeChanged.emit(transedTime)

        if self.musicTime == 0:
            return
        self.playWidgets.slider.setValue(currentTime / self.musicTime * 1000)
        self.setLyricEvent(position)

    def stateChangedEvent(self):
        if self.state() == 0 and self.playList.mediaCount() == 0 and self.playWidgets.pauseButton.isVisible():
            self.playWidgets.stopEvent(self)

    def setLyricEvent(self, position):

        t = QTime(0, 0, 0)
        t = t.addMSecs(int(position))
        lycF = ''
        lycL = ''
        lycM = ''
        if self.lrc_lst:
            lenOfLrc = len(self.lrc_lst)
            for i in range(lenOfLrc):
                if t.toString("mm:ss") in self.lrc_lst[i][0]:
                    t1 = t
                    if i < lenOfLrc - 1:
                        x = self.lrc_lst[i + 1][0].replace('[', '')
                        x = x.replace(']', '')
                        t1 = QTime().fromString(x, 'mm:ss.z')
                        intervel = t.msecsTo(t1)
                    else:
                        t1 = QTime().fromString('00:10.99')
                        intervel = 3000
                    self.parent.desktopLyric.stopMask()
                    self.parent.desktopLyric.setText(self.lrc_lst[i][1], intervel)
                    self.parent.desktopLyric.startMask()
                    if i > 0:
                        lycM = self.lrc_lst[i - 1][1]
                    j = 0
                    while (j < i - 1):
                        lycF += self.lrc_lst[j][1] + '\n'
                        j += 1
                    j = i
                    while (j < lenOfLrc - 1):
                        lycL += self.lrc_lst[j + 1][1] + '\n'
                        j += 1
                    break


class _TableWidget(QTableWidget):

    def __init__(self, parent=None):
        super(_TableWidget, self).__init__()
        self.parent = parent
        self.setMouseTracking(True)
        self.entered.connect(self.itemHover)
        self.viewportEntered.connect(self.viewportHover)
        self.itemDualBackground = QColor.fromRgb(45, 47, 51)
        self.itemBackground = QColor.fromRgb(48, 50, 54)
        self.itemHoverBackground = QColor.fromRgb(50, 52, 56)
        self.itemHoverColor = QColor.fromRgb(226, 226, 226)
        self.itemColor = QColor.fromRgb(220, 221, 228)
        self.itemColor2 = QColor.fromRgb(95, 95, 99)
        self.itemHoverRow = -1
        self.items = []
        self.setActions()

    def setActions(self):
        self.actionClear = QAction('清空', self)
        self.actionClear.triggered.connect(self.clears)
        self.actionRemove = QAction('从列表中删除', self)
        self.actionRemove.triggered.connect(self.remove)

    def _setItems(self, row):
        self.items.append(self.item(row, 0))
        self.items.append(self.item(row, 1))
        self.items.append(self.item(row, 2))

        for i in self.items:
            if i:
                i.setForeground(self.itemHoverColor)
                i.setBackground(self.itemHoverBackground)
        self.itemHoverRow = row

    def itemHover(self, rowItem):
        row = rowItem.row()

        if row != self.itemHoverRow:
            if not self.items:
                self._setItems(row)
            else:
                if row % 2 == 0:
                    for i in self.items:
                        i.setBackground(self.itemDualBackground)
                else:
                    for i in self.items:
                        i.setBackground(self.itemBackground)
                self.items[0].setForeground(self.itemColor)
                for i in self.items[1:]:
                    i.setForeground(self.itemColor2)
                self.items = []
                self._setItems(row)

    def viewportHover(self):
        if not self.items:
            return

        self.items[0].setForeground(self.itemColor)
        for i in self.items[1:]:
            i.setForeground(self.itemColor2)
        self.items = []
        self.itemHoverRow = -1

    def clears(self):
        self.clearContents()
        self.setRowCount(0)
        playlist = self.parent.parent.player.playList
        playlist.clear()
        playlist.removeList = []
        playlist.mediaList = {}
        self.parent.musicList = []
        self.parent.currentRow = -1
        self.parent.allRow = 0
        self.items = []
        self.itemHoverRow = -1
        self.parent.parent.player.stop()

    def remove(self):
        row = self.currentRow()
        player = self.parent.parent.player
        self.removeRow(row)
        self.parent.musicList = self.parent.musicList[:row] + self.parent.musicList[row + 1:]
        player.playList.removeMedias(row)
        self.parent.allRow -= 1
        if self.itemHoverRow == row:
            self.itemHoverRow = -1
            self.items = []

    def contextMenuEvent(self, event):
        item = self.itemAt(self.mapFromGlobal(QCursor.pos()))
        self.menu = QMenu(self)
        if not item:
            self.menu.addAction(self.actionClear)
        else:
            self.menu.addAction(self.actionRemove)
            self.menu.addAction(self.actionClear)
        self.menu.exec_(QCursor.pos())


class _MediaPlaylist(QObject):
    musicsCookiesFolder = 'cookies/mediaPlaylist/musics.cks'
    mediaListCookiesFolder = 'cookies/mediaPlaylist/mediaList.cks'
    allCookiesFolder = [musicsCookiesFolder, mediaListCookiesFolder]

    def __init__(self, parent=None):
        super(_MediaPlaylist, self).__init__()
        self.parent = parent
        self.playWidgets = self.parent.parent
        self.duration = 0
        self.musics = []
        self.currentIndex = 0
        self.mediaList = {}
        self.single = 1
        self.Loop = 3
        self.Random = 4
        self.myModel = 3

    def setInitConnection(self):
        self.parent.mediaStatusChanged.connect(self.mediaStatusChangedEvent)

    def addMedias(self, url, data):
        self.parent.setMedia(url)
        self.musics.append(url)
        self.currentIndex = len(self.musics) - 1
        self.mediaList[url.canonicalUrl().toString()] = data
        self.parent.playMusic()
        self.playWidgets.parent.systemTray.setToolTip('{0}-{1}'.format(data['name'], data['author']))

    def addAllMedias(self, datas):
        for i in datas:
            self.musics.append(i['url'])
            self.mediaList[i['url']] = i

    def clear(self):
        self.musics = []
        self.mediaList = {}
        self.currentIndex = 0
        self.duration = 0

    def mediaCount(self):
        return len(self.musics)

    def removeMedias(self, row):
        self.musics.pop(row)

    def next(self):
        if self.myModel == 4:
            index = random.randint(0, len(self.musics) - 1)
            self.setCurrentIndex(index)
            return
        if self.currentIndex + 1 >= len(self.musics):
            self.currentIndex = 0
        else:
            self.currentIndex += 1
        self.play()

    def previous(self):
        if self.myModel == 4:
            index = random.randint(0, len(self.musics) - 1)
            self.setCurrentIndex(index)
            return
        if self.currentIndex - 1 <= -1:
            self.currentIndex = 0
        else:
            self.currentIndex -= 1
        self.play()

    def play(self):
        try:
            media = self.musics[self.currentIndex]
        except:
            logger.error("unknow error. musics info: {0}".format(self.musics))
            return

        if type(media) == str:
            if 'http' in media or 'file' in media:
                content = QMediaContent(QUrl(media))
            else:
                content = QMediaContent(QUrl.fromLocalFile(media))
            self.musics = self.musics[:self.currentIndex] + [content] + self.musics[self.currentIndex + 1:]
            media = content
        self.parent.setMedia(media)
        self.parent.playMusic()
        self.tabMusicEvent()

    def setCurrentIndex(self, index):
        try:
            media = self.musics[index]
        except:
            logger.error("unknow error. musics info: {0}".format(self.musics))
            return

        if type(media) == str:
            if 'http' in media or 'file' in media:
                content = QMediaContent(QUrl(media))
            else:
                content = QMediaContent(QUrl.fromLocalFile(media))
            self.musics = self.musics[:index] + [content] + self.musics[index + 1:]
            self.parent.setMedia(content)
        else:
            self.parent.setMedia(media)
        self.parent.playMusic()
        self.tabMusicEvent()
        self.currentIndex = index

    def setPlaybackMode(self, model: int):
        self.myModel = model

    def setShortInfo(self, musicInfo):
        name = musicInfo['name']
        author = musicInfo['author']
        pic = musicInfo['music_img']
        self.playWidgets.currentMusic.setShortInfo(name, author, pic)

    def setLyric(self, musicInfo):
        name = musicInfo['name']
        author = musicInfo['author']
        if self.playWidgets.currentMusic.detailInfo.isVisible() or self.playWidgets.desktopLyric.isVisible():
            self.playWidgets.desktopLyric.setText("{}-{}".format(name, author), 0)
            self.playWidgets.currentMusic.setDetailInfo()

    def setSystemTrayTip(self, tipMessage):
        self.playWidgets.parent.systemTray.setToolTip(tipMessage)

    def updatePlayTimes(self, musicInfo):
        if 'http(' in musicInfo['url']:
            return
        self.playWidgets.parent.db.addPlayTimesById(musicInfo['music_id'],
                                                    musicInfo['name'],
                                                    musicInfo['author'])

    @checkFolder(allCookiesFolder)
    def saveCookies(self):
        with open(self.musicsCookiesFolder, 'wb') as f:
            for row, data in enumerate(self.musics):
                if type(data) == QMediaContent:
                    url = data.canonicalUrl().toString()
                    self.musics[row] = url

            pickle.dump(self.musics, f)
        with open(self.mediaListCookiesFolder, 'wb') as f:
            pickle.dump(self.mediaList, f)

    @checkFolder(allCookiesFolder)
    def loadCookies(self):
        with open(self.mediaListCookiesFolder, 'rb') as f:
            self.mediaList = pickle.load(f)

        with open(self.musicsCookiesFolder, 'rb') as f:
            self.musics = pickle.load(f)
            for index, url in enumerate(self.musics):
                if '?vkey=' in url:
                    musicInfo = self.mediaList.pop(url)
                    url = url.split('qq.com/')
                    url2 = url[1].split('?vkey=')[0]
                    url = '{0}'.format(qqApi.sip) + url2 + '?vkey={0}'.format(qqApi.key) + \
                          '&guid={0}'.format(qqApi.guid)
                    self.musics[index] = url.format(qqApi.sip, qqApi.key)
                    self.mediaList[url] = musicInfo

    def mediaStatusChangedEvent(self, status):
        if status == 7:
            if self.myModel == 3:
                self.next()
            elif self.myModel == 4:
                index = random.randint(0, len(self.musics) - 1)
                self.setCurrentIndex(index)
            else:
                self.parent.play()

    def tabMusicEvent(self):
        try:
            indexUrl = self.parent.currentMedia().canonicalUrl().toString()
            musicInfo = self.mediaList[indexUrl]
            self.setShortInfo(musicInfo)
            self.setLyric(musicInfo)
            self.setSystemTrayTip('{0}-{1}'.format(musicInfo['name'], musicInfo['author']))
            self.updatePlayTimes(musicInfo)
        except:
            logger.error("tabbing music error has ignored. index{0} mediaList{1}".format(indexUrl, self.mediaList),
                         exc_info=True)


class _LyricLabel(QLabel):
    __slots__ = ('myTime', 'myLyric', 'myOrder', 'parent')

    def __init__(self, myTime, lyric, myOrder, signal, parent=None):
        super(_LyricLabel, self).__init__(lyric)
        self.setObjectName('lyric')
        self.parent = parent
        self.myTime = myTime[:myTime.rfind('.')]
        self.myLyric = lyric
        self.myOrder = myOrder
        signal.connect(self.lightMe)
        self.setMinimumHeight(40)
        self.setTextInteractionFlags(Qt.TextSelectableByMouse)

    def lightMe(self, currentTime):
        if currentTime == self.myTime:
            if self.parent.order != self.myOrder:
                self.parent.unLightLyric()
                self.parent.order = self.myOrder
                self.parent.slide()
                self.setText('<font color="white">{0}</font>'.format(self.myLyric))

    def unLightMe(self):
        self.setText(self.myLyric)


class DesktopLyric(QDialog):
    def __init__(self, parent=None):
        super(DesktopLyric, self).__init__()
        self.lyric = QString('Lyric Show.')
        self.intervel = 0
        self.maskRect = QRectF(0, 0, 0, 0)
        self.maskWidth = 0
        self.widthBlock = 0
        self.t = QTimer()
        self.screen = QApplication.desktop().availableGeometry()
        self.setObjectName(_fromUtf8("Dialog"))
        self.setWindowFlags(
            Qt.CustomizeWindowHint | Qt.FramelessWindowHint | Qt.Dialog | Qt.WindowStaysOnTopHint | Qt.Tool)
        self.setMinimumHeight(65)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.handle = lyric_handle(self)
        self.verticalLayout = QVBoxLayout(self)
        self.verticalLayout.setSpacing(0)
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
        self.font = QFont(_fromUtf8('微软雅黑, verdana'), 50)
        self.font.setPixelSize(50)
        self.handle.lyricmoved.connect(self.newPos)
        self.t.timeout.connect(self.changeMask)

    def changeMask(self):
        self.maskWidth += self.widthBlock
        self.update()

    def setText(self, s, intervel=0):
        self.lyric = s
        self.intervel = intervel
        self.maskWidth = 0
        self.update()

    def hideLyric(self):
        self.hide()

    def leaveEvent(self, event):
        self.handle.leaveEvent(event)

    def show(self):

        super(DesktopLyric, self).show()

    def hide(self):
        super(DesktopLyric, self).hide()
        self.handle.hide()

    def enterEvent(self, event):
        pass

    def newPos(self, p):
        self.move(self.pos().x() + p.x(), self.pos().y() + p.y())

    def startMask(self):
        self.t.start(100)

    def stopMask(self):
        self.t.stop()
        self.update()

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setFont(self.font)
        linear = QLinearGradient(QPoint(self.rect().topLeft()), QPoint(self.rect().bottomLeft()))
        linear.setStart(0, 10)
        linear.setFinalStop(0, 50)
        linear.setColorAt(0.1, QColor(14, 179, 255));
        linear.setColorAt(0.5, QColor(154, 232, 255));
        linear.setColorAt(0.9, QColor(14, 179, 255));
        linear2 = QLinearGradient(QPoint(self.rect().topLeft()), QPoint(self.rect().bottomLeft()))
        linear2.setStart(0, 10)
        linear2.setFinalStop(0, 50)
        linear2.setColorAt(0.1, QColor(222, 54, 4));
        linear2.setColorAt(0.5, QColor(255, 172, 116));
        linear2.setColorAt(0.9, QColor(222, 54, 4));
        painter.setPen(QColor(0, 0, 0, 200));
        painter.drawText(QRect(1, 1, self.screen.width(), 60), Qt.AlignHCenter | Qt.AlignVCenter, self.lyric)
        painter.setPen(QColor('transparent'));
        self.textRect = painter.drawText(QRect(0, 0, self.screen.width(), 60), Qt.AlignHCenter | Qt.AlignVCenter,
                                         self.lyric)
        painter.setPen(QPen(linear, 0))
        painter.drawText(self.textRect, Qt.AlignLeft | Qt.AlignVCenter, self.lyric)
        if self.intervel != 0:
            self.widthBlock = self.textRect.width() / (self.intervel / 150.0)
        else:
            self.widthBlock = 0
        self.maskRect = QRectF(self.textRect.x(), self.textRect.y(), self.textRect.width(), self.textRect.height())
        self.maskRect.setWidth(self.maskWidth)
        painter.setPen(QPen(linear2, 0));
        painter.drawText(self.maskRect, Qt.AlignLeft | Qt.AlignVCenter, self.lyric)

    def mousePressEvent(self, event):
        if event.buttons() == Qt.LeftButton:
            self.m_drag = True
            self.m_DragPosition = event.globalPos() - self.pos()
            event.accept()

    def mouseMoveEvent(self, event):
        try:
            if event.buttons() and Qt.LeftButton:
                self.move(event.globalPos() - self.m_DragPosition)
                event.accept()
        except AttributeError:
            pass

    def mouseReleaseEvent(self, event):
        if event.buttons() == Qt.LeftButton:
            self.m_drag = False


class lyric_handle(QDialog):
    lyricmoved = pyqtSignal(QPoint)

    def __init__(self, parent=None):
        super(lyric_handle, self).__init__(parent)
        self.timer = QTimer()
        self.setObjectName(_fromUtf8("Dialog"))
        self.setWindowFlags(
            Qt.CustomizeWindowHint | Qt.FramelessWindowHint | Qt.Dialog | Qt.WindowStaysOnTopHint | Qt.Tool)
        self.setStyleSheet('QDialog { background: #2c7ec8; border: 0px solid black;}')
        self.horiLayout = QHBoxLayout(self)
        self.horiLayout.setSpacing(5)
        self.horiLayout.setContentsMargins(0, 0, 0, 0)
        self.horiLayout.setObjectName(_fromUtf8("horiLayout"))
        self.handler = QLabel(self)
        self.handler.setToolTip('Move Lyric')
        self.handler.setPixmap(QPixmap(':/icons/handler.png'))
        self.handler.setMouseTracking(True)
        self.lockBt = PushButton2(self)
        self.lockBt.setToolTip('Unlocked')
        self.lockBt.loadPixmap(QPixmap(':/icons/unlock.png'))
        self.hideBt = PushButton2(self)
        self.hideBt.setToolTip('Hide Lyric')
        self.hideBt.loadPixmap(QPixmap(':/icons/close.png').copy(48, 0, 16, 16))
        self.lockBt.setCheckable(True)
        self.horiLayout.addWidget(self.handler)
        self.horiLayout.addWidget(self.lockBt)
        self.horiLayout.addWidget(self.hideBt)
        self.lockBt.clicked.connect(self.lockLyric)
        self.hideBt.clicked.connect(self.hideLyric)
        self.timer.timeout.connect(self.hide)

    def lockLyric(self):
        if self.lockBt.isChecked():
            self.lockBt.loadPixmap(QPixmap(':/icons/lock.png'))
            self.lockBt.setToolTip('Locked')
            self.lockBt.update()
        else:
            self.lockBt.loadPixmap(QPixmap(':/icons/unlock.png'))
            self.lockBt.setToolTip('Unlocked')
            self.lockBt.update()

    def hideLyric(self):
        self.parent().lyrichide.emit()
        self.parent().hide()
        self.hide()

    def isInTitle(self, xPos, yPos):
        if self.lockBt.isChecked():
            return False
        else:
            return yPos <= self.height() and 0 <= xPos <= self.handler.width()

    def moveEvent(self, event):
        self.lyricmoved.emit(QPoint(event.pos() - event.oldPos()))

    def enterEvent(self, event):
        self.setFocus()
        self.timer.stop()

    def leaveEvent(self, event):
        self.timer.stop()
        self.timer.start(3000)


def getLyric(rawLyric):
    lrc = rawLyric
    r1 = re.compile("\[(\d{2}:\d{2}(.\d+)?)\]")
    r2 = re.compile("\[\d+:+.+\](.*)")
    r3 = re.compile("\[offset:(-?\d+)\]")
    item = []
    lrc_lst = []
    offset = 0
    for line in lrc:
        times = r1.findall(line)
        lrc_words = r2.findall(line)
        if lrc_words:
            lrc_words = lrc_words[0]
        else:
            lrc_words = []

        if len(lrc_words) and lrc_words[0].rstrip():
            for i in times:
                item.append(i[0])
                item.append(lrc_words)
                lrc_lst.append(item)
                item = []
    lrc_lst.sort()
    return lrc_lst


if __name__ == '__main__':
    import sys

    os.chdir('..')
    app = QApplication(sys.argv)
    main = PlayWidgets()
    main.show()
    sys.exit(app.exec_())
